﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
namespace Primitive.Threading.Sync
{
    class MultiWebRequets
    {
        //这个辅助类用于协调所有的异步操作
        private AsyncCoordinator m_ac = new AsyncCoordinator();
        //想要查询的Web服务器集合
        private List<WebRequest> m_requests = new List<WebRequest>
        {
             WebRequest.Create("http://www.baidu.com"),
             WebRequest.Create("http://www.sohu.com"),
        };
        //创建响应数组:每个请求一个响应
        private List<WebResponse> m_results = new List<WebResponse>();

        public MultiWebRequets(int timeout = Timeout.Infinite)
        {
            
            int n = 0;
            //以异步方式一次性发起所有请求
            foreach (WebRequest request in m_requests)
            {
                m_ac.AboutToBegin(1);
                request.BeginGetResponse(EndGetResponse, n);
               
                n++;
            }
            //告诉辅助类所有操作都已发起,并在所有操作完成
            //调用Cancel或者发生了超时的时候调用AllDone
            m_ac.AllBegun(AllDone, timeout);
            Console.WriteLine("call all done thread id is :" + Thread.CurrentThread.ManagedThreadId.ToString());
        }
        //每个Web服务器响应时,就调用这个方法
        private void EndGetResponse(IAsyncResult result)
        {
            //获取与请求对应的索引
            int n = (int)result.AsyncState;
            //将响应保存在和请求相同的索引中
            m_results.Add(
                m_requests[n].EndGetResponse(result));
            //告诉辅助类Web服务器已经响应
            m_ac.JustEnded();

        }

        //这个方法在所有Web服务器都响应,
        //调用Cancel或者发生了超时的时候调用
        private void AllDone(CoordinationStatus status)
        {
           
            switch (status)
            {
                case CoordinationStatus.Cancel:
                    {
                        Console.WriteLine("The operation was canceld.");
                    }
                    break;
                case CoordinationStatus.TimeOut:
                    {
                        Console.WriteLine("The operation time-out.");
                    }
                    break;
                case CoordinationStatus.AllDone:
                    {
                        Console.WriteLine("Here are the results from all the Web Servers.");
                        Console.WriteLine("excute real all done thread id is :" + Thread.CurrentThread.ManagedThreadId.ToString());
                        for (int n = 0; n < m_requests.Count; n++)
                        {
                            Console.WriteLine("{0} returned {1} bytes.", m_results[n].ResponseUri, m_results[n].ContentLength);
                        }
                    }
                    break;
            }
        }
    }

    class AsyncCoordinator
    {
        private int m_opCount = 1;
        private int m_statusReported = 0;
        private Action<CoordinationStatus> m_callback;
        private Timer m_timer;

        //必须在调用一个BeginXXX方法之前调用这个方法
        public void AboutToBegin(int number = 1)
        {
            Interlocked.Add(ref m_opCount, number);
        }
        //必须在调用一个EndXXX方法之前调用这个方法
        public void JustEnded()
        {
            if (Interlocked.Decrement(ref m_opCount) == 0)
            {
                Console.WriteLine("call report all done thread id is :" + Thread.CurrentThread.ManagedThreadId.ToString());
                ReportStatus(CoordinationStatus.AllDone);
            }
        }

        private void ReportStatus(CoordinationStatus status)
        {
            if (Interlocked.Exchange(ref m_statusReported, 1) == 0)
            {
                Console.WriteLine("report all done thread id is :" + Thread.CurrentThread.ManagedThreadId.ToString());
                m_callback(status);
            }
        }

        public void AllBegun(Action<CoordinationStatus> AllDone, int timeout = Timeout.Infinite)
        {
            m_callback = AllDone;
            if (timeout != Timeout.Infinite)
            {
                m_timer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);
            }
            JustEnded();
        }

        private void TimeExpired(object o)
        {
            ReportStatus(CoordinationStatus.TimeOut);
        }

        public void Cancle()
        {
            ReportStatus(CoordinationStatus.Cancel);
        }
    }

    enum CoordinationStatus
    {
         Cancel,TimeOut,AllDone
    }
}
